package fr.pentalog.ctc.controller.page;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.validation.Valid;

import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import ro.peoplecentric.ctc.common.entity.Category;
import ro.peoplecentric.ctc.common.entity.Item;
import ro.peoplecentric.ctc.common.entity.ItemType;
import ro.peoplecentric.ctc.common.entity.Region;
import ro.peoplecentric.ctc.common.exception.ApplicationException;
import ro.peoplecentric.ctc.common.service.CategoryService;
import ro.peoplecentric.ctc.common.service.ItemService;
import ro.peoplecentric.ctc.common.service.RegionService;

import com.google.gson.Gson;

import fr.pentalog.ctc.model.AddEditForm;
import fr.pentalog.ctc.model.ErrorMessage;
import fr.pentalog.ctc.model.Property;
import fr.pentalog.ctc.model.ValidationResponse;
import fr.pentalog.ctc.util.WebAppConstants;

/**
 * @author Danut Chindris
 * @since September 19, 2013
 */

@Controller
public class AddEditController {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	public static final String ADD_EDIT_VIEW_NAME = "pages/add-edit";

	private static final String ARTICLE_ATTRIBUTE = "article";
	private static final String REGIONS_ATTRIBUTE = "regions";
	private static final String CATEGORIES_ATTRIBUTE = "categories";
	private static final String TYPES_ATTRIBUTE = "types";

	private static final String ACTIVE_FORM_PAGE = "activeForm";
	private static final String ADD_FORM_PAGE = "addForm";
	private static final String EDIT_FORM_PAGE = "editForm";

	@Resource
	private ItemService itemService;

	@Resource
	private RegionService regionService;

	@Resource
	private CategoryService categoryService;

	@Resource
	private MessageSource messageSource;

	@RequestMapping(value = { "/add" }, method = RequestMethod.GET)
	public String showAddPage(Model model) {
		model.addAttribute(WebAppConstants.ARTICLE_ATTRIBUTE, new AddEditForm());
		Map<String, String> regions = this.getRegions();
		model.addAttribute(REGIONS_ATTRIBUTE, regions);
		Map<String, String> categories = this.getCategories();
		model.addAttribute(CATEGORIES_ATTRIBUTE, categories);
		List<Property> types = this.getTypes();
		model.addAttribute(TYPES_ATTRIBUTE, types);

		model.addAttribute(ACTIVE_FORM_PAGE, ADD_FORM_PAGE);
		return ADD_EDIT_VIEW_NAME;
	}

	@RequestMapping(value = { "/edit" }, method = RequestMethod.GET)
	public String showEditPage(Model model, @RequestParam Long id) {
		Item item = null;
		try {
			item = itemService.getById(id);
		}
		catch (ApplicationException e) {
			logger.error(e.getMessage(), e);
		}
		if (item == null) {
			return WebAppConstants.ITEMS_REDIRECT_VIEW_NAME;
		}
		AddEditForm article = this.populateForm(item);
		model.addAttribute(WebAppConstants.ARTICLE_ATTRIBUTE, article);
		Map<String, String> regions = this.getRegions();
		model.addAttribute(REGIONS_ATTRIBUTE, regions);
		Map<String, String> categories = this.getCategories();
		model.addAttribute(CATEGORIES_ATTRIBUTE, categories);
		List<Property> types = this.getTypes();
		model.addAttribute(TYPES_ATTRIBUTE, types);

		model.addAttribute(ACTIVE_FORM_PAGE, EDIT_FORM_PAGE);
		return ADD_EDIT_VIEW_NAME;
	}

	private Map<String, String> getRegions() {
		Map<String, String> regions = new LinkedHashMap<String, String>();
		regions.put(WebAppConstants.EMPTY_STRING, WebAppConstants.EMPTY_STRING);
		try {
			List<Region> regionObjects = regionService.getAll();
			if (regionObjects != null) {
				for (Region region : regionObjects) {
					if (region != null) {
						regions.put(String.valueOf(region.getId()), region.getName());
					}
				}
			}
		}
		catch (ApplicationException e) {
			logger.error(e.getMessage(), e);
		}
		return regions;
	}

	private Map<String, String> getCategories() {
		Map<String, String> categories = new LinkedHashMap<String, String>();
		categories.put(WebAppConstants.EMPTY_STRING, WebAppConstants.EMPTY_STRING);
		try {
			List<Category> categoryObjects = categoryService.getAll();
			if (categoryObjects != null) {
				for (Category category : categoryObjects) {
					if (category != null) {
						categories.put(String.valueOf(category.getId()), category.getName());
					}
				}
			}
		}
		catch (ApplicationException e) {
			logger.error(e.getMessage(), e);
		}
		return categories;
	}

	private List<Property> getTypes() {
		List<Property> types = new ArrayList<Property>();
		types.add(new Property(String.valueOf(ItemType.Individual.ordinal()), messageSource.getMessage(ItemType.Individual.getMessageKey(), null, null)));
		types.add(new Property(String.valueOf(ItemType.Business.ordinal()), messageSource.getMessage(ItemType.Business.getMessageKey(), null, null)));
		return types;
	}

	private AddEditForm populateForm(Item item) {
		AddEditForm article = new AddEditForm();
		article.setId(item.getId());
		if (item.getRegion() != null) {
			article.setRegion(String.valueOf(item.getRegion().getId()));
		}
		article.setPostalCode(item.getPostalCode());
		if (item.getCategory() != null) {
			article.setCategory(String.valueOf(item.getCategory().getId()));
		}
		if (item.getType() != null) {
			article.setType(String.valueOf(item.getType().ordinal()));
		}
		article.setName(item.getName());
		article.setEmail(item.getEmail());
		article.setPhone(item.getPhone());
		article.setTitle(item.getTitle());
		article.setDescription(item.getDescription());
		article.setPrice(String.valueOf(item.getPrice()));
		article.setPictureUrl(item.getPictureUrl());
		return article;
	}

	@RequestMapping(value = { "/add" }, method = RequestMethod.POST)
	public String add() {
		return WebAppConstants.ITEMS_REDIRECT_VIEW_NAME;
	}

	@RequestMapping(value = { "/edit" }, method = RequestMethod.POST)
	public String edit() {
		return WebAppConstants.ITEMS_REDIRECT_VIEW_NAME;
	}

	@RequestMapping(value = { "/add-edit-validation" }, method = RequestMethod.POST)
	@ResponseBody public String performAddEditValidation(Model model, @ModelAttribute(value = ARTICLE_ATTRIBUTE) @Valid AddEditForm article, BindingResult result) {
		ValidationResponse validationResponse = new ValidationResponse();
		if (!result.hasErrors()) {
			validationResponse.setStatus(WebAppConstants.SUCCESS);
		}
		else {
			validationResponse.setStatus(WebAppConstants.FAILURE);
			List<FieldError> allErrors = result.getFieldErrors();
			List<ErrorMessage> errorMesages = new ArrayList<ErrorMessage>();
			for (FieldError objectError : allErrors) {
				errorMesages.add(new ErrorMessage(objectError.getField(), StringEscapeUtils.escapeJava(messageSource.getMessage(objectError.getDefaultMessage(), null, null))));
			}
			validationResponse.setErrorMessageList(errorMesages);
		}
		Gson gson = new Gson();
		String json = gson.toJson(validationResponse);
		return StringEscapeUtils.unescapeJava(json);
	}

	@RequestMapping(value = "/perform-add-edit", method = RequestMethod.POST)
	@ResponseBody public String performAddEdit(@ModelAttribute(value = ARTICLE_ATTRIBUTE) AddEditForm article) {
		article.setId(article.getId());
		ValidationResponse validationResponse = new ValidationResponse();
		try {
			this.saveItem(article);
			validationResponse.setStatus(WebAppConstants.SUCCESS);
		}
		catch (ApplicationException e) {
			logger.error(e.getMessage(), e);
			validationResponse.setStatus(WebAppConstants.FAILURE);
			List<ErrorMessage> errorMessageList = new ArrayList<ErrorMessage>();
			errorMessageList.add(new ErrorMessage(null, e.getMessage()));
			validationResponse.setErrorMessageList(errorMessageList);
		}
		Gson gson = new Gson();
        return gson.toJson(validationResponse);
	}

	private void saveItem(AddEditForm article) throws ApplicationException {
		Item item = new Item();
		Item oldItem = null;
		if (article.getId() != null) {
			item.setId(article.getId());
			oldItem = itemService.getById(article.getId());
		}
		Region region = this.getRegion(article.getRegion());
		item.setRegion(region);
		item.setPostalCode(article.getPostalCode());
		Category category = this.getCategory(article.getCategory());
		item.setCategory(category);
		item.setType(ItemType.values()[Integer.parseInt(article.getType())]);
		item.setName(article.getName());
		item.setEmail(article.getEmail());
		item.setPhone(article.getPhone());
		item.setTitle(article.getTitle());
		item.setDescription(article.getDescription());
		item.setPrice(Double.parseDouble(article.getPrice()));
		if (article.getPictureUrl() != null && !article.getPictureUrl().isEmpty()) {
			item.setPictureUrl(article.getPictureUrl());
		}
		else if (oldItem != null && oldItem.getPictureUrl() != null && !oldItem.getPictureUrl().isEmpty()) {
			item.setPictureUrl(oldItem.getPictureUrl());
		}
		itemService.saveItem(item);
	}

	private Region getRegion(String regionId) throws ApplicationException {
		//TODO no getById() method exposed in RegionService; this is a temporary solution
		List<Region> regions = regionService.getAll();
		for (Region currentRegion : regions) {
			if (currentRegion.getId().longValue() == Long.parseLong(regionId)) {
				return currentRegion;
			}
		}
		return null;
	}

	private Category getCategory(String categoryId) throws ApplicationException {
		//TODO no getById() method exposed in CategoryService; this is a temporary solution
		List<Category> categories = categoryService.getAll();
		for (Category currentCategory : categories) {
			if (currentCategory.getId().longValue() == Long.parseLong(categoryId)) {
				return currentCategory;
			}
		}
		return null;
	}
}
